/*-------------------------------------------------------------------------
 *
 * freepage.h
 *	  页组织的自由内存管理。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/utils/freepage.h
 *
 *-------------------------------------------------------------------------
 */

#ifndef FREEPAGE_H
#define FREEPAGE_H

#include "storage/lwlock.h"
#include "utils/relptr.h"

/* 前向声明。 */
typedef struct FreePageSpanLeader FreePageSpanLeader;
typedef struct FreePageBtree FreePageBtree;
typedef struct FreePageManager FreePageManager;

/*
 * PostgreSQL 通常使用 8kB 页进行大多数操作，但许多常见
 * 架构/操作系统组合使用 4kB 页大小进行内存
 * 分配，因此我们在这里也这样做。
 */
#define FPM_PAGE_SIZE			4096

/*
 * 每个自由列表除了最后一个都只包含单一特定
 * 大小的块。所有更大的块都放在最后一个列表中。在某种意义上，这看起来
 * 像是浪费，因为大多数分配都是在少数几种常见大小中，但它
 * 意味着小分配可以简单地弹出相关列表的头部
 * 而无需担心我们在那儿找到的对象是否恰好是
 * 正确的大小（因为我们知道它一定是）。
 */
#define FPM_NUM_FREELISTS		129

/* 定义相对指针类型。 */
relptr_declare(FreePageBtree, RelptrFreePageBtree);
relptr_declare(FreePageManager, RelptrFreePageManager);
relptr_declare(FreePageSpanLeader, RelptrFreePageSpanLeader);

/* 管理空闲页所需的一切（见 freepage.c） */
struct FreePageManager
{
	RelptrFreePageManager self;
	RelptrFreePageBtree btree_root;
	RelptrFreePageSpanLeader btree_recycle;
	unsigned	btree_depth;
	unsigned	btree_recycle_count;
	Size		singleton_first_page;
	Size		singleton_npages;
	Size		contiguous_pages;
	bool		contiguous_pages_dirty;
	RelptrFreePageSpanLeader freelist[FPM_NUM_FREELISTS];
#ifdef FPM_EXTRA_ASSERTS
	/* 仅用于调试，页数减去获取的页数。 */
	Size		free_pages;
#endif
};

/* 将页码（以 Size 表示）和指针之间转换的宏。 */
#define fpm_page_to_pointer(base, page) \
	(AssertVariableIsOfTypeMacro(page, Size), \
	 (base) + FPM_PAGE_SIZE * (page))
#define fpm_pointer_to_page(base, ptr)		\
	(((Size) (((char *) (ptr)) - (base))) / FPM_PAGE_SIZE)

/* 将分配大小转换为页数的宏。 */
#define fpm_size_to_pages(sz) \
	(((sz) + FPM_PAGE_SIZE - 1) / FPM_PAGE_SIZE)

/* 检查绝对和相对指针对齐的宏。 */
#define fpm_pointer_is_page_aligned(base, ptr)		\
	(((Size) (((char *) (ptr)) - (base))) % FPM_PAGE_SIZE == 0)
#define fpm_relptr_is_page_aligned(base, relptr)		\
	(relptr_offset(relptr) % FPM_PAGE_SIZE == 0)

/* 查找包含 FreePageManager 的段基地址的宏。 */
#define fpm_segment_base(fpm)	\
	(((char *) fpm) - relptr_offset(fpm->self))

/* 访问 FreePageManager 最大连续页数的宏。 */
#define fpm_largest(fpm) \
	(fpm->contiguous_pages)

/* 操作空闲页映射的函数。 */
extern void FreePageManagerInitialize(FreePageManager *fpm, char *base);
extern bool FreePageManagerGet(FreePageManager *fpm, Size npages,
							   Size *first_page);
extern void FreePageManagerPut(FreePageManager *fpm, Size first_page,
							   Size npages);
extern char *FreePageManagerDump(FreePageManager *fpm);

#endif							/* FREEPAGE_H */
